import Head from 'next/head';
import TableOptionsTable from '../../../components/prop-tables/TableOptionsTable';
import ColumnOptionsTable from '../../../components/prop-tables/ColumnOptionsTable';
import StateOptionsTable from '../../../components/prop-tables/StateOptionsTable';
import AggregationExample from '../../../examples/aggregation-and-grouping';
import AggregationMultiExample from '../../../examples/aggregation-multi';

<Head>
  <title>
    {'Aggregation and Grouping Feature Guide - Mantine React Table Docs'}
  </title>
  <meta
    name="description"
    content="How to use and customize the aggregation and grouping features of Mantine React Table"
  />
</Head>

## Aggregation and Grouping Feature Guide

Mantine React Table has built-in grouping and aggregation features. There are options for both automatic client-side grouping and aggregation, as well as manual server-side grouping and aggregation. This guide will walk you through the different options and how to use and customize them.

### Relevant Table Options

<TableOptionsTable
  onlyOptions={
    new Set([
      'aggregationFns',
      'enableExpandAll',
      'enableGrouping',
      'enableStickyFooter',
      'getGroupedRowModel',
      'groupedColumnMode',
      'manualGrouping',
      'mantineToolbarAlertBannerBadgeProps',
      'onGroupingChange',
      'positionToolbarAlertBanner',
    ])
  }
/>

### Relevant Column Options

<ColumnOptionsTable
  onlyOptions={
    new Set(['AggregatedCell', 'Footer', 'GroupedCell', 'enableGrouping'])
  }
/>

### Relevant State

<StateOptionsTable onlyOptions={new Set(['grouping', 'expanded'])} />

### Enable Grouping

To enable grouping, set the `enableGrouping` table option to `true`. This will both add a drag handle button so that columns can be dragged to the dropzone to be grouped and will add an entry column actions menu to group or ungroup a column.

```jsx
const table = useMantineReactTable({
  columns,
  data,
  enableGrouping: true, //enable grouping
});
```

#### Disable Grouping Per Column

```jsx
const columns = [
  {
    accessorKey: 'name',
    header: 'Name',
    enableGrouping: false, // disable grouping for this column
  },
  {
    accessorKey: 'age',
    header: 'Age',
  },
];

const table = useMantineReactTable({
  columns,
  data,
  enableGrouping: true, //enable grouping
});

return <MantineReactTable table={table} />;
```

#### Hide Drag Buttons for Grouping

If you do not want the drag buttons that come with the grouping feature, you can independently disable them without disabling the grouping feature entirely by setting the `enableColumnDragging` table option to `false`.

```jsx
const table = useMantineReactTable({
  columns,
  data,
  enableGrouping: true,
  enableColumnDragging: false, //do not show drag handle buttons, but still show grouping options in column actions menu
});
```

### Group Columns by Default

If you want columns to be grouped by default, you can set the `grouping` state in either the `initialState` or `state` table options.

```jsx
const table = useMantineReactTable({
  columns,
  data,
  enableGrouping: true,
  initialState: { grouping: ['location', 'department'] }, //group by location and department by default
});
```

### Expand Grouped Rows by Default

In addition to grouping columns by default, you may also want those grouped rows to be expanded and visible by default, too. You can do this by setting the `expanded` state to `true` in either the `initialState` or `state` table options.

```jsx
const table = useMantineReactTable({
  columns,
  data,
  enableGrouping: true,
  initialState: {
    grouping: ['location', 'department'], //group by location and department by default and expand grouped rows
    expanded: true, //show grouped rows by default
  },
});
```

### Aggregation on Grouped Rows

One of the cool features of Mantine React Table is that it can automatically aggregate the data in grouped rows. To enable this, you must specify both an `aggregationFn` and an `AggregatedCell` render option on a column definition.

#### Built-in Aggregation Functions

There are several built-in aggregation functions available that you can use. They are:

- `count` - Finds the number of rows in a group
- `extent` - Finds the minimum and maximum values of a group of rows
- `max` - Finds the maximum value of a group of rows
- `mean` - Finds the average value of a group of rows
- `median` - Finds the median value of a group of rows
- `min` - Finds the minimum value of a group of rows
- `sum` - sums the values of a group of rows
- `uniqueCount` - Finds the number of unique values of a group of rows
- `unique` - Finds the unique values of a group of rows

_All of these built-in aggregation functions are from [TanStack Table](https://tanstack.com/table/v8/docs/api/features/grouping#aggregation-functions)_

```jsx
const columns = [
  {
    accessorKey: 'team', //grouped by team in initial state below
    header: 'Team',
  },
  {
    accessorKey: 'player',
    header: 'Player',
  },
  {
    accessorKey: 'points',
    header: 'Points',
    aggregationFn: 'sum', //calc total points for each team by adding up all the points for each player on the team
    AggregatedCell: ({ cell }) => <div>Team Score: {cell.getValue()}</div>,
  },
];

const table = useMantineReactTable({
  columns,
  data,
  enableGrouping: true,
  initialState: { grouping: ['team'], expanded: true },
});

return <MantineReactTable table={table} />;
```

#### Custom Aggregation Functions

If none of these pre-built aggregation functions work for you, you can also pass in a custom aggregation function. The aggregation function will be passed in an array of values from the column that you are aggregating. It should return a single value that will be displayed in the aggregated cell.

If you are specifying a custom aggregation function, it must implement the following type:

```jsx
export type AggregationFn<TData extends AnyData> = (
  getLeafRows: () => Row<TData>[],
  getChildRows: () => Row<TData>[]
) => any
```

### Aggregation on All Rows in Footer

Mantine React Table does not automatically aggregate all rows for you to calculate totals for the entire table. However, it is still easy enough to do this manually and add in your custom calculations into the `footer` or `Footer` of a column definition. It is recommended that you do any necessary aggregation calculations on your data in a useMemo hook before passing it to the columns footer in your columns definition.

```jsx
//calculate the total points for all players in the table in a useMemo hook
const averageScore = useMemo(() => {
  const totalPoints = data.reduce((acc, row) => acc + row.points, 0);
  const totalPlayers = data.length;
  return totalPoints / totalPlayers;
}, [data]);

const columns = [
  {
    accessorKey: 'name',
    header: 'Name',
  },
  {
    accessorKey: 'score',
    header: 'Score',
    Footer: () => <div>Average Score: {averageScore}</div>, //do not do calculations in render, do them in useMemo hook and pass them in here
  },
];
```

> Please remember to perform heavy aggregation calculations in a useMemo hook to avoid unnecessary re-renders!

### Custom Cell Renders for Aggregation and Grouping

There are a few custom cell render overrides that you should be aware of when using grouping and aggregation features.

#### AggregatedCell Column Option

"Aggregation Cells" are cells in an aggregated row (not a normal data row) that can display aggregates (avg, sum, etc.) of the data in a group. The cell that the table is grouped on, however, is not an Aggregate Cell, but rather a [GroupedCell](#groupedcell-column-option).

You can specify the custom render for these cells with the `AggregatedCell` render option on a column definition.

```jsx
const columns = [
  {
    accessorKey: 'points',
    header: 'Points',
    aggregationFn: 'sum',
    AggregatedCell: ({ cell }) => <div>Total Score: {cell.getValue()}</div>,
  },
];
```

#### GroupedCell Column Option

"Grouped Cells" are cells in a grouped row (not a normal data row) that by default display the value that the rows are grouped on and the number of rows in the group. You can override the default render for these cells with the `GroupedCell` render option on a column definition.

```jsx
const columns = [
  {
    accessorKey: 'team',
    header: 'Team',
    GroupedCell: ({ cell }) => <div>Team: {cell.getValue()}</div>,
  },
];
```

#### PlaceholderCell Column Option

"Placeholder Cells" are cells that are usually meant to be empty in grouped rows and columns. They are simply rendered with a value of `null` by default, but you can override the default render for these cells with the `PlaceholderCell` render option on a column definition.

```jsx
const columns = [
  {
    accessorKey: 'team',
    header: 'Team',
    PlaceholderCell: ({ cell, row }) => (
      <div>{row.original.someOtherRowValue}</div>
    ),
  },
];
```

### Aggregation/Grouping Example

<AggregationExample />

### Multiple Aggregations Per column

You may want to calculate more than one aggregation per column. If so, you can specify an array of `aggregationFn`s, and then reference the aggregation results from an array in the `AggregatedCell` render option.

```jsx
const columns = [
  {
    header: 'Salary',
    accessorKey: 'salary',
    aggregationFn: ['count', 'mean'], //multiple aggregation functions
    AggregatedCell: ({ cell, table }) => (
      <div>
        {/*get the count from the first aggregation*/}
        <div>Count: {cell.getValue()[0]}</div>
        {/*get the average from the second aggregation*/}
        <div>Average Salary: {cell.getValue()[1]}</div>
      </div>
    ),
  },
];
```

<AggregationMultiExample />

### Manual Grouping

Manual Grouping means that the `data` that you pass to the table is already grouped and aggregated, and you do not want Mantine React Table to do any of the grouping or aggregation for you. This is useful if you are using a backend API to do the grouping and aggregation for you, and you just want to display the results. However, you will need to put your data in the specific format that the `expanding` features understand.
